package uk.ac.lkl.common.associator.ui;




import java.awt.AWTEvent;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;

import java.awt.event.AWTEventListener;
//import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
//import java.awt.event.KeyListener;
//import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import java.util.ArrayList;

import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;


public class AssociationViewPanelLinker {

    private static int JOIN_THRESHOLD;

    static {
        setJoinThreshold(30);
    }

    private NewAssociatorView associatorView;

    private AssociationViewPanel pressPanel;

    private Point linkPoint = new Point();

    private ArrayList<AssociationViewPanel> targetPanels;

    private MouseInputAdapter mouseInputAdapter = new MouseInputAdapter() {

        public void mousePressed(MouseEvent e) {
            processMousePressed(e);
        }


        public void mouseDragged(MouseEvent e) {
            processMouseDragged(e);
        }


        public void mouseReleased(MouseEvent e) {
            processMouseReleased(e);
        }

    };


    public AssociationViewPanelLinker(
                                      NewAssociatorView associatorView) {
        this.associatorView = associatorView;
        Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {

            public void eventDispatched(AWTEvent event) {
                processEventDispatched(event);
            }

        }, AWTEvent.KEY_EVENT_MASK);
    }


    private void processEventDispatched(AWTEvent event) {
        if (event.getID() != KeyEvent.KEY_PRESSED)
            return;

        KeyEvent keyEvent = (KeyEvent) event;
        if (keyEvent.getKeyCode() != KeyEvent.VK_ESCAPE)
            return;

        pressPanel = null;
        associatorView.repaint();
    }


    public <O> void addViewPanel(AssociationViewPanel<O> viewPanel) {
        viewPanel.addMouseListener(mouseInputAdapter);
        viewPanel.addMouseMotionListener(mouseInputAdapter);
    }


    private void processMousePressed(MouseEvent e) {
        if (e.getButton() != MouseEvent.BUTTON1)
            return;
        AssociationViewPanel panel = (AssociationViewPanel) e.getSource();
        pressPanel = panel;
        targetPanels = associatorView.getLinkableComponents(panel);
    }


    private void processMouseDragged(MouseEvent e) {
        if (pressPanel == null)
            return;

        AssociationViewPanel panel = (AssociationViewPanel) e.getSource();
        int x = e.getX();
        int y = e.getY();

        linkPoint = SwingUtilities.convertPoint(panel, x, y, associatorView);
        associatorView.repaint();
    }


    private void processMouseReleased(MouseEvent e) {
        final Point pressPanelCenter = new Point();

        if (pressPanel == null)
            return;

        AssociationViewPanel panel = (AssociationViewPanel) e.getSource();
        int x = e.getX();
        int y = e.getY();

        linkPoint = SwingUtilities.convertPoint(panel, x, y, associatorView);

        Rectangle pressPanelBounds = pressPanel.getBounds();
        pressPanelCenter.setLocation(pressPanelBounds.x
                + pressPanelBounds.width / 2, pressPanelBounds.y
                + pressPanelBounds.height / 2);

        Double minimumDistance = null;
        AssociationViewPanel closestTarget = null;

        for (AssociationViewPanel targetPanel : targetPanels) {
            // assumes not nested
            Rectangle bounds = targetPanel.getBounds();

            Point targetLocation;

            if (bounds.x + bounds.width / 2 > pressPanelCenter.x)
                targetLocation = new Point(bounds.x, bounds.y + bounds.height
                        / 2);
            else
                targetLocation = new Point(bounds.x + bounds.width, bounds.y
                        + bounds.height / 2);

            double distance = linkPoint.distance(targetLocation);
            if (minimumDistance == null || distance < minimumDistance) {
                minimumDistance = distance;
                closestTarget = targetPanel;
            }
        }

        if (closestTarget != null && minimumDistance < JOIN_THRESHOLD)
            associatorView.associate(pressPanel, closestTarget);

        associatorView.repaint();
        pressPanel = null;
    }


    /**
     * Paints in coordinate space of the associator view.
     * 
     * @param g2
     * 
     */
    public void paint(Graphics g) {
        if (pressPanel == null)
            return;

        Rectangle bounds = pressPanel.getBounds();

        Graphics2D g2 = (Graphics2D) g;

        int x = bounds.x;
        int y = bounds.y + bounds.height / 2;

        if (bounds.x + bounds.width / 2 < linkPoint.x)
            x += bounds.width;

        g2.drawLine(x, y, linkPoint.x, linkPoint.y);

    }


    public static void setJoinThreshold(int joinThreshold) {
        AssociationViewPanelLinker.JOIN_THRESHOLD = joinThreshold;
    }

}
